El siguiente proyecto de investigación se basa en los datos obtenidos en Datos Abiertos Gobierno de México con la finalidad de realizar un modelo que pueda predecir la cantidad de delitos que ocurrirán por entidad, así como su tipo de delito, basado en datos históricos de delincuencia municipal mensual de 2015 a octubre de 2024.
De manera inicial se busca el conocer los datos con los que se trabajara para poder visualizar datos estadísticos descriptivos, tendencias, relaciones e identificar las principales variables que se usaran en la creación del modelo predictivo.
#Importo librerias inicialmente para EDA o Exploratory Data Analysis
import pandas as pd
import matplotlib.pyplot as plt
from IPython.display import display
#Extraemos el archivo
data_delic = pd.read_csv("IDM_NM_oct24.csv", encoding='latin-1') # Prueba con 'latin-1'
#Visualizamos las primeras filas del archivo
data_delic.head()
| Año | Clave_Ent | Entidad | Cve. Municipio | Municipio | Bien jurídico afectado | Tipo de delito | Subtipo de delito | Modalidad | Enero | ... | Marzo | Abril | Mayo | Junio | Julio | Agosto | Septiembre | Octubre | Noviembre | Diciembre | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 2015 | 1 | Aguascalientes | 1001 | Aguascalientes | La vida y la Integridad corporal | Homicidio | Homicidio doloso | Con arma de fuego | 2 | ... | 1 | 1 | 0 | 1 | 1 | 0 | 2 | 1 | 0.0 | 1.0 |
| 1 | 2015 | 1 | Aguascalientes | 1001 | Aguascalientes | La vida y la Integridad corporal | Homicidio | Homicidio doloso | Con arma blanca | 1 | ... | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 0.0 | 0.0 |
| 2 | 2015 | 1 | Aguascalientes | 1001 | Aguascalientes | La vida y la Integridad corporal | Homicidio | Homicidio doloso | Con otro elemento | 0 | ... | 1 | 1 | 3 | 2 | 0 | 1 | 2 | 0 | 0.0 | 0.0 |
| 3 | 2015 | 1 | Aguascalientes | 1001 | Aguascalientes | La vida y la Integridad corporal | Homicidio | Homicidio doloso | No especificado | 1 | ... | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0.0 |
| 4 | 2015 | 1 | Aguascalientes | 1001 | Aguascalientes | La vida y la Integridad corporal | Homicidio | Homicidio culposo | Con arma de fuego | 0 | ... | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0.0 |
5 rows × 21 columns
data_delic.shape
(2319072, 21)
#Columnas de las cuales se compone el archivo
data_delic.columns
Index(['Año', 'Clave_Ent', 'Entidad', 'Cve. Municipio', 'Municipio',
'Bien jurídico afectado', 'Tipo de delito', 'Subtipo de delito',
'Modalidad', 'Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio',
'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'],
dtype='object')
#Información de como esta compuesto el archivo
data_delic.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 2319072 entries, 0 to 2319071 Data columns (total 21 columns): # Column Dtype --- ------ ----- 0 Año int64 1 Clave_Ent int64 2 Entidad object 3 Cve. Municipio int64 4 Municipio object 5 Bien jurídico afectado object 6 Tipo de delito object 7 Subtipo de delito object 8 Modalidad object 9 Enero int64 10 Febrero int64 11 Marzo int64 12 Abril int64 13 Mayo int64 14 Junio int64 15 Julio int64 16 Agosto int64 17 Septiembre int64 18 Octubre int64 19 Noviembre float64 20 Diciembre float64 dtypes: float64(2), int64(13), object(6) memory usage: 371.6+ MB
#Identificar los años incluidos
data_delic["Año"].unique()
array([2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023, 2024],
dtype=int64)
El archivo incluye los meses en cada columna con un formato ancho ("wide"), sin embargo para realizar el análisis de datos es conveniente tener los meses como filas para poder crear visualizaciones y modelado.
#Se realiza una división por Mes y se agrega una columna que muestra la "Cantidad" de delitos según cada modalidad
data_delic_long = pd.melt(
data_delic,
id_vars=[
"Año", "Clave_Ent", "Entidad", "Cve. Municipio", "Municipio",
"Bien jurídico afectado", "Tipo de delito", "Subtipo de delito", "Modalidad"
],
value_vars=[
"Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio",
"Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre"
],
var_name="Mes",
value_name="Cantidad"
)
# Se realizara un "mapeo" para poder representar los meses de manera numerica
meses = {
"Enero": 1, "Febrero": 2, "Marzo": 3, "Abril": 4, "Mayo": 5, "Junio": 6,
"Julio": 7, "Agosto": 8, "Septiembre": 9, "Octubre": 10, "Noviembre": 11, "Diciembre": 12
}
# Mapear los nombres de los meses a números
data_delic_long["Mes"] = data_delic_long["Mes"].map(meses)
data_delic_long.head()
| Año | Clave_Ent | Entidad | Cve. Municipio | Municipio | Bien jurídico afectado | Tipo de delito | Subtipo de delito | Modalidad | Mes | Cantidad | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 2015 | 1 | Aguascalientes | 1001 | Aguascalientes | La vida y la Integridad corporal | Homicidio | Homicidio doloso | Con arma de fuego | 1 | 2.0 |
| 1 | 2015 | 1 | Aguascalientes | 1001 | Aguascalientes | La vida y la Integridad corporal | Homicidio | Homicidio doloso | Con arma blanca | 1 | 1.0 |
| 2 | 2015 | 1 | Aguascalientes | 1001 | Aguascalientes | La vida y la Integridad corporal | Homicidio | Homicidio doloso | Con otro elemento | 1 | 0.0 |
| 3 | 2015 | 1 | Aguascalientes | 1001 | Aguascalientes | La vida y la Integridad corporal | Homicidio | Homicidio doloso | No especificado | 1 | 1.0 |
| 4 | 2015 | 1 | Aguascalientes | 1001 | Aguascalientes | La vida y la Integridad corporal | Homicidio | Homicidio culposo | Con arma de fuego | 1 | 0.0 |
data_delic_long.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 27828864 entries, 0 to 27828863 Data columns (total 11 columns): # Column Dtype --- ------ ----- 0 Año int64 1 Clave_Ent int64 2 Entidad object 3 Cve. Municipio int64 4 Municipio object 5 Bien jurídico afectado object 6 Tipo de delito object 7 Subtipo de delito object 8 Modalidad object 9 Mes int64 10 Cantidad float64 dtypes: float64(1), int64(4), object(6) memory usage: 2.3+ GB
data_delic_long.describe()
| Año | Clave_Ent | Cve. Municipio | Mes | Cantidad | |
|---|---|---|---|---|---|
| count | 2.782886e+07 | 2.782886e+07 | 2.782886e+07 | 2.782886e+07 | 2.734220e+07 |
| mean | 2.019688e+03 | 1.920339e+01 | 1.930702e+04 | 6.500000e+00 | 7.094455e-01 |
| std | 2.802761e+00 | 7.523347e+00 | 7.539501e+03 | 3.452053e+00 | 8.386157e+00 |
| min | 2.015000e+03 | 1.000000e+00 | 1.001000e+03 | 1.000000e+00 | -1.000000e+00 |
| 25% | 2.017000e+03 | 1.400000e+01 | 1.405400e+04 | 3.750000e+00 | 0.000000e+00 |
| 50% | 2.020000e+03 | 2.000000e+01 | 2.021600e+04 | 6.500000e+00 | 0.000000e+00 |
| 75% | 2.022000e+03 | 2.400000e+01 | 2.405700e+04 | 9.250000e+00 | 0.000000e+00 |
| max | 2.024000e+03 | 3.200000e+01 | 3.205800e+04 | 1.200000e+01 | 2.143000e+03 |
#El formato ancho constaba de 21 columnas y 2,319,072 filas
data_delic.shape
(2319072, 21)
#El formato "largo" a usarse consta de 11 columnas y 278,288,864 filas
data_delic_long.shape
(27828864, 11)
La transformación realizada de formato "ancho" a "largo" redujo el numero de columnas en -10 pero realizo un aumento de filas en 276 millones aprox.
Dado que estamos trabajando con datos temporales se crea la columna "Fecha" para representar el tiempo en que sucede cada fila. El día no se encuentra presente en el set de datos por lo que se pondra por default el primero de cada mes.
data_delic_long["Fecha"] = pd.to_datetime(
data_delic_long["Año"].astype(str) + '-' + data_delic_long["Mes"].astype(str) + '-01',
format='%Y-%m-%d', # Formato año-mes-día
errors="coerce" # Coerción de errores en caso de valores mal formados
)
#Existia un ruido visual en los datos al gráficar por lo que se filtra todas las fechas posterioes a octubre 2024
data_delic_long = data_delic_long[data_delic_long["Fecha"] <= "2024-10-31"]
Para conocer los datos vamos a comenzar respondiendo preguntas como ¿Cuantos tipos de delitos están registrados?, ¿Cuales son los delitos con más frecuencia? ,etc...
columnas_interes = [
"Entidad",
"Municipio",
"Bien jurídico afectado",
"Tipo de delito",
"Subtipo de delito",
"Modalidad"
]
conteo_unicos = data_delic_long[columnas_interes].nunique()
print(conteo_unicos)
Entidad 32 Municipio 2334 Bien jurídico afectado 7 Tipo de delito 40 Subtipo de delito 55 Modalidad 59 dtype: int64
El registro histórico de delincuencia de Enero 2015 a Octubre 2024 esta compuesto por la información delictiva de 32 Estados, 2334 Municipios dividido por:
¿Cuáles son los delitos más comunes?
# Top tipo de delito
top_tipos_delito = (
data_delic_long.groupby("Tipo de delito")["Cantidad"]
.sum()
.sort_values(ascending=False)
.head(10)
.reset_index()
)
#Top subtipos de delito
top_subtipos_delito = (
data_delic_long.groupby("Subtipo de delito")["Cantidad"]
.sum()
.sort_values(ascending=False)
.head(10)
.reset_index()
)
#Top por modalidad
top_modalidades = (
data_delic_long.groupby("Modalidad")["Cantidad"]
.sum()
.sort_values(ascending=False)
.head(10)
.reset_index()
)
# Orden por bien afectado
top_bienes_afectados = (
data_delic_long.groupby("Bien jurídico afectado")["Cantidad"]
.sum()
.sort_values(ascending=False)
.reset_index()
)
# Formato a tabla para visualización
def formatear_tabla(data, nombre):
print(f"\n--- {nombre} ---")
return data.style.format({"Cantidad": "{:,.0f}"})
display(formatear_tabla(top_tipos_delito, "Top 10 Tipos de Delitos"))
display(formatear_tabla(top_subtipos_delito, "Top 10 Subtipos de Delitos"))
display(formatear_tabla(top_modalidades, "Top 10 Modalidades"))
display(formatear_tabla(top_bienes_afectados, "Top 10 Bienes Jurídicos Afectados"))
--- Top 10 Tipos de Delitos ---
| Tipo de delito | Cantidad | |
|---|---|---|
| 0 | Robo | 6,578,412 |
| 1 | Violencia familiar | 2,107,776 |
| 2 | Lesiones | 2,028,612 |
| 3 | Otros delitos del Fuero Común | 1,830,298 |
| 4 | Daño a la propiedad | 1,316,460 |
| 5 | Amenazas | 1,045,332 |
| 6 | Fraude | 809,114 |
| 7 | Narcomenudeo | 649,390 |
| 8 | Homicidio | 405,704 |
| 9 | Despojo | 269,092 |
--- Top 10 Subtipos de Delitos ---
| Subtipo de delito | Cantidad | |
|---|---|---|
| 0 | Violencia familiar | 2,107,776 |
| 1 | Otros robos | 1,926,104 |
| 2 | Otros delitos del Fuero Común | 1,830,298 |
| 3 | Robo de vehículo automotor | 1,622,678 |
| 4 | Lesiones dolosas | 1,529,661 |
| 5 | Daño a la propiedad | 1,316,460 |
| 6 | Amenazas | 1,045,332 |
| 7 | Robo a negocio | 908,250 |
| 8 | Fraude | 809,114 |
| 9 | Robo a transeúnte en vía pública | 724,174 |
--- Top 10 Modalidades ---
| Modalidad | Cantidad | |
|---|---|---|
| 0 | Sin violencia | 3,227,551 |
| 1 | Violencia familiar | 2,107,776 |
| 2 | Otros delitos del Fuero Común | 1,830,298 |
| 3 | Con violencia | 1,712,684 |
| 4 | Con otro elemento | 1,366,771 |
| 5 | Daño a la propiedad | 1,316,460 |
| 6 | Amenazas | 1,045,332 |
| 7 | Robo de coche de 4 ruedas Sin violencia | 843,263 |
| 8 | Fraude | 809,114 |
| 9 | Narcomenudeo | 649,390 |
--- Top 10 Bienes Jurídicos Afectados ---
| Bien jurídico afectado | Cantidad | |
|---|---|---|
| 0 | El patrimonio | 9,427,042 |
| 1 | Otros bienes jurídicos afectados (del fuero común) | 4,069,408 |
| 2 | La vida y la Integridad corporal | 2,538,472 |
| 3 | La familia | 2,502,983 |
| 4 | La libertad y la seguridad sexual | 567,671 |
| 5 | Libertad personal | 206,013 |
| 6 | La sociedad | 86,208 |
Para un primer visual y conocer como se ha comportado el indice delictivo a lo largo de los ultimos años crearemos una linea de tiempo tomando en cuenta la suma de todos los tipos de delitos registrados.
import seaborn as sns
#Agrupamos los datos por Fecha y Cantidad de delitos
delitos_por_fecha = data_delic_long.groupby(["Fecha"])["Cantidad"].sum().reset_index()
plt.figure(figsize=(12, 6))
sns.lineplot(data=delitos_por_fecha, x="Fecha", y="Cantidad", marker="o")
plt.title("Cantidad de delitos a lo largo del tiempo")
plt.xlabel("Fecha")
plt.ylabel("Cantidad de delitos")
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()
Observando el gráfico anterior podemos visualizar 3 puntos a simple vista:
Top 10 tipos de delitos en los ultimos años
data_agrupada = (
data_delic_long.groupby(["Fecha", "Tipo de delito"])["Cantidad"]
.sum()
.reset_index()
)
top_tipos_delito = (
data_delic_long.groupby("Tipo de delito")["Cantidad"]
.sum()
.sort_values(ascending=False)
.head(10)
.index
)
data_top10 = data_agrupada[data_agrupada["Tipo de delito"].isin(top_tipos_delito)]
fig, axes = plt.subplots(5, 2, figsize=(20, 20)) # Grid de 5 filas x 2 columnas
axes = axes.flatten() # Aplanar para iterar más fácilmente
for i, delito in enumerate(top_tipos_delito):
ax = axes[i]
# Filtrar los datos del delito actual
data_delito = data_top10[data_top10["Tipo de delito"] == delito]
# Graficar
sns.lineplot(data=data_delito, x="Fecha", y="Cantidad", ax=ax, marker="o")
ax.set_title(delito)
ax.set_xlabel("Fecha")
ax.set_ylabel("Cantidad")
ax.tick_params(axis="x", rotation=45) # Rotar etiquetas del eje X
# Ajustar diseño para evitar solapamiento
plt.tight_layout()
plt.show()
La tendencia al alza se puede observar en 9 de los 10 delitos más comunes registrados teniendo como la excepción "Robo" el cual es el delito con mayor numero de incidencias, a continuación se muestra un desglose de dicho delito según Subtipo de Delito y Modalidad.
#Se filtra el set de datos original por tipo de Delito "Robo"
data_robo=data_delic_long[data_delic_long["Tipo de delito"]=="Robo"]
data_robo.head()
| Año | Clave_Ent | Entidad | Cve. Municipio | Municipio | Bien jurídico afectado | Tipo de delito | Subtipo de delito | Modalidad | Mes | Cantidad | Fecha | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 39 | 2015 | 1 | Aguascalientes | 1001 | Aguascalientes | El patrimonio | Robo | Robo a casa habitación | Con violencia | 1 | 4.0 | 2015-01-01 |
| 40 | 2015 | 1 | Aguascalientes | 1001 | Aguascalientes | El patrimonio | Robo | Robo a casa habitación | Sin violencia | 1 | 172.0 | 2015-01-01 |
| 41 | 2015 | 1 | Aguascalientes | 1001 | Aguascalientes | El patrimonio | Robo | Robo de vehículo automotor | Robo de coche de 4 ruedas Con violencia | 1 | 4.0 | 2015-01-01 |
| 42 | 2015 | 1 | Aguascalientes | 1001 | Aguascalientes | El patrimonio | Robo | Robo de vehículo automotor | Robo de coche de 4 ruedas Sin violencia | 1 | 114.0 | 2015-01-01 |
| 43 | 2015 | 1 | Aguascalientes | 1001 | Aguascalientes | El patrimonio | Robo | Robo de vehículo automotor | Robo de motocicleta Con violencia | 1 | 1.0 | 2015-01-01 |
robos_por_año=data_robo.groupby("Año")["Cantidad"].sum().reset_index
robos_por_año
<bound method Series.reset_index of Año 2015 652647.0 2016 690048.0 2017 801099.0 2018 810592.0 2019 766096.0 2020 604275.0 2021 609271.0 2022 609635.0 2023 581510.0 2024 453239.0 Name: Cantidad, dtype: float64>
#Robos por subtipo
robos_por_subtipo=data_robo.groupby("Subtipo de delito")["Cantidad"].sum().reset_index()
robos_por_subtipo=robos_por_subtipo.sort_values(by="Cantidad",ascending=False)
robos_por_subtipo
| Subtipo de delito | Cantidad | |
|---|---|---|
| 0 | Otros robos | 1926104.0 |
| 10 | Robo de vehículo automotor | 1622678.0 |
| 3 | Robo a negocio | 908250.0 |
| 5 | Robo a transeúnte en vía pública | 724174.0 |
| 1 | Robo a casa habitación | 715908.0 |
| 7 | Robo de autopartes | 173699.0 |
| 12 | Robo en transporte público colectivo | 142252.0 |
| 11 | Robo en transporte individual | 133829.0 |
| 6 | Robo a transportista | 95822.0 |
| 8 | Robo de ganado | 48466.0 |
| 4 | Robo a transeúnte en espacio abierto al público | 44493.0 |
| 13 | Robo en transporte público individual | 23715.0 |
| 9 | Robo de maquinaria | 15499.0 |
| 2 | Robo a institución bancaria | 3523.0 |
#Robos por Modalidad
robos_por_modalidad=data_robo.groupby("Modalidad")["Cantidad"].sum().reset_index()
robos_por_modalidad=robos_por_modalidad.sort_values(by="Cantidad",ascending=False)
robos_por_modalidad
| Modalidad | Cantidad | |
|---|---|---|
| 13 | Sin violencia | 3227551.0 |
| 0 | Con violencia | 1712684.0 |
| 4 | Robo de coche de 4 ruedas Sin violencia | 843263.0 |
| 3 | Robo de coche de 4 ruedas Con violencia | 456859.0 |
| 10 | Robo de motocicleta Sin violencia | 233440.0 |
| 9 | Robo de motocicleta Con violencia | 88864.0 |
| 2 | Robo de cables, tubos y otros objetos destinad... | 9049.0 |
| 8 | Robo de herramienta industrial o agrícola Sin ... | 3791.0 |
| 7 | Robo de herramienta industrial o agrícola Con ... | 1094.0 |
| 12 | Robo de tractores Sin violencia | 685.0 |
| 11 | Robo de tractores Con violencia | 467.0 |
| 1 | Robo de cables, tubos y otros objetos destinad... | 413.0 |
| 6 | Robo de embarcaciones pequeñas y grandes Sin v... | 204.0 |
| 5 | Robo de embarcaciones pequeñas y grandes Con v... | 48.0 |
Cantidad de delitos por Estado
Es de interes también el conocer la distribución de delitos por Entidad, en el siguiente gráfico encontramos que en los ultimos años la cantidad de delitos el Estado de méxico y Ciudad de México conforman la mayor frecuencia en base a los delitos totales.
# Gráfico de la cantidad de delitos por entidad
delitos_por_entidad = data_delic_long.groupby(["Entidad"])["Cantidad"].sum().reset_index()
plt.figure(figsize=(14, 7))
sns.barplot(x="Entidad", y="Cantidad", data=delitos_por_entidad, estimator="sum", ci=None)
plt.title("Cantidad de Delitos por Entidad")
plt.xlabel("Entidad")
plt.ylabel("Cantidad de Delitos")
plt.xticks(rotation=90)
plt.tight_layout()
plt.show()
C:\Users\diego\AppData\Local\Temp\ipykernel_21608\3468840994.py:6: FutureWarning: The `ci` parameter is deprecated. Use `errorbar=None` for the same effect. sns.barplot(x="Entidad", y="Cantidad", data=delitos_por_entidad, estimator="sum", ci=None)
delitos_por_entidad
| Entidad | Cantidad | |
|---|---|---|
| 0 | Aguascalientes | 345134.0 |
| 1 | Baja California | 1061823.0 |
| 2 | Baja California Sur | 217612.0 |
| 3 | Campeche | 81147.0 |
| 4 | Chiapas | 197919.0 |
| 5 | Chihuahua | 683165.0 |
| 6 | Ciudad de México | 2114668.0 |
| 7 | Coahuila de Zaragoza | 545071.0 |
| 8 | Colima | 226953.0 |
| 9 | Durango | 279362.0 |
| 10 | Guanajuato | 1266477.0 |
| 11 | Guerrero | 285579.0 |
| 12 | Hidalgo | 449537.0 |
| 13 | Jalisco | 1338937.0 |
| 14 | Michoacán de Ocampo | 423751.0 |
| 15 | Morelos | 439960.0 |
| 16 | México | 3517626.0 |
| 17 | Nayarit | 64072.0 |
| 18 | Nuevo León | 852284.0 |
| 19 | Oaxaca | 353692.0 |
| 20 | Puebla | 667387.0 |
| 21 | Querétaro | 525600.0 |
| 22 | Quintana Roo | 397245.0 |
| 23 | San Luis Potosí | 438688.0 |
| 24 | Sinaloa | 261502.0 |
| 25 | Sonora | 303098.0 |
| 26 | Tabasco | 512915.0 |
| 27 | Tamaulipas | 408414.0 |
| 28 | Tlaxcala | 52669.0 |
| 29 | Veracruz de Ignacio de la Llave | 714651.0 |
| 30 | Yucatán | 151902.0 |
| 31 | Zacatecas | 218957.0 |
La estacionalidad en series de tiempo hace referencia a los patrones cíclicos que ocurren en intervalos regulares dentro de los datos, en este caso, en cada año parece observarse un ciclo que se repite en el aumento y disminución de los delitos. Estos patrones son importantes a considerar al momento de decidir que modelo predictivo se usará. En las gráficas de series de tiempo podemos observar una tendencia alcista así como un patron repetitivo donde a final de año existe una baja en los delitos y un alza repentina en los primeros meses del año. A continuación se realizarán pruebas estadísticas para comprobar si existe estacionalidad estadísticamente significativa en nuestros datos.
data_delic_long.info()
<class 'pandas.core.frame.DataFrame'> DatetimeIndex: 27342196 entries, 2015-01-01 to 2023-12-01 Data columns (total 11 columns): # Column Dtype --- ------ ----- 0 Año int64 1 Clave_Ent int64 2 Entidad object 3 Cve. Municipio int64 4 Municipio object 5 Bien jurídico afectado object 6 Tipo de delito object 7 Subtipo de delito object 8 Modalidad object 9 Mes int32 10 Cantidad float64 dtypes: float64(1), int32(1), int64(3), object(6) memory usage: 2.3+ GB
import statsmodels.api as sm
# Establecer la 'Fecha' como índice de la serie temporal
data_delic_long.set_index('Fecha', inplace=True)
--------------------------------------------------------------------------- KeyError Traceback (most recent call last) ~\AppData\Local\Temp\ipykernel_20856\1506702948.py in ?() 3 4 5 6 # Establecer la 'Fecha' como índice de la serie temporal ----> 7 data_delic_long.set_index('Fecha', inplace=True) ~\anaconda3\lib\site-packages\pandas\core\frame.py in ?(self, keys, drop, append, inplace, verify_integrity) 5855 if not found: 5856 missing.append(col) 5857 5858 if missing: -> 5859 raise KeyError(f"None of {missing} are in the columns") 5860 5861 if inplace: 5862 frame = self KeyError: "None of ['Fecha'] are in the columns"
Prueba de Raíz Unitaria: Dickey-Fuller
En análisis de series de tiempo, una de las pruebas más comunes para evaluar la estacionariedad es la prueba de Dickey-Fuller aumentada (ADF). Esta prueba nos permite determinar si una serie de tiempo tiene una raíz unitaria, lo que implicaría que la serie es no estacionaria.
La prueba de Dickey-Fuller examina si una serie de tiempo tiene una raíz unitaria en su proceso generador. Si existe una raíz unitaria, los datos no serán estacionarios, lo que implica que la serie tiene una tendencia a largo plazo (ya sea creciente o decreciente).
Prueba de Dickey-Fuller La prueba de Dickey-Fuller es una regresión que sigue el siguiente modelo:
Δ 𝑦
𝛼 + 𝛽 𝑡 + 𝛾 𝑦 𝑡 − 1 + ∑
1 𝑝 𝜃 𝑖 Δ 𝑦 𝑡 − 𝑖 + 𝜖 𝑡 Δy t =α+βt+γy t−1
Donde:
Δ 𝑦
𝑦 𝑡 − 𝑦 𝑡 − 1 Δy t =y t −y t−1 es la diferencia de primer orden de la serie de tiempo. 𝑡 t es la tendencia lineal. 𝑦 𝑡 − 1 y t−1 es el valor rezagado de la serie de tiempo. 𝜖 𝑡 ϵ t es el término de error blanco. 𝛾 γ es el parámetro clave. Si
0 γ=0, la serie tiene una raíz unitaria y es no estacionaria. Hipótesis de la Prueba Hipótesis nula ( 𝐻 0 H 0 ): La serie de tiempo tiene una raíz unitaria (es no estacionaria).
Hipótesis alternativa ( 𝐻 1 H 1 ): La serie de tiempo no tiene una raíz unitaria (es estacionaria).
Interpretación:
from statsmodels.tsa.stattools import adfuller
# Prueba de Dickey-Fuller
result = adfuller(delitos_por_fecha['Cantidad'])
print(f'Valor p: {result[1]}')
Valor p: 0.16094478950584457
import plotly.express as px
# Agrupar por fecha y calcular la cantidad total de delitos
delitos_por_fecha = data_delic_long.groupby(["Fecha"])["Cantidad"].sum().reset_index()
# Crear el gráfico interactivo
fig = px.line(
delitos_por_fecha,
x="Fecha",
y="Cantidad",
title="Cantidad de delitos a lo largo del tiempo",
labels={"Fecha": "Fecha", "Cantidad": "Cantidad de delitos"},
markers=True, # Para incluir los puntos en la línea
template="plotly_white" # Tema más limpio
)
# Mejorar las etiquetas de eje y formato de fechas
fig.update_xaxes(dtick="M1", tickformat="%b %Y", showgrid=True) # Marcas mensuales
fig.update_yaxes(showgrid=True)
fig.update_traces(line=dict(color="blue")) # Personalización del color de línea
# Mostrar el gráfico interactivo
fig.show()
# Agrupar por año, mes y calcular la cantidad total de delitos
delitos_por_fecha_mes = data_delic_long.groupby(["Mes"])["Cantidad"].sum().reset_index()
# Crear un gráfico de línea
plt.figure(figsize=(12, 6))
sns.lineplot(data=delitos_por_fecha, x="Mes", y="Cantidad", marker="o")
plt.title("Cantidad de delitos a lo largo del tiempo")
plt.xlabel("Mes")
plt.ylabel("Cantidad de delitos")
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()
--------------------------------------------------------------------------- ValueError Traceback (most recent call last) Cell In[59], line 6 4 # Crear un gráfico de línea 5 plt.figure(figsize=(12, 6)) ----> 6 sns.lineplot(data=delitos_por_fecha, x="Mes", y="Cantidad", marker="o") 7 plt.title("Cantidad de delitos a lo largo del tiempo") 8 plt.xlabel("Mes") File ~\anaconda3\lib\site-packages\seaborn\relational.py:618, in lineplot(data, x, y, hue, size, style, units, palette, hue_order, hue_norm, sizes, size_order, size_norm, dashes, markers, style_order, estimator, errorbar, n_boot, seed, orient, sort, err_style, err_kws, legend, ci, ax, **kwargs) 615 errorbar = _deprecate_ci(errorbar, ci) 617 variables = _LinePlotter.get_semantics(locals()) --> 618 p = _LinePlotter( 619 data=data, variables=variables, 620 estimator=estimator, n_boot=n_boot, seed=seed, errorbar=errorbar, 621 sort=sort, orient=orient, err_style=err_style, err_kws=err_kws, 622 legend=legend, 623 ) 625 p.map_hue(palette=palette, order=hue_order, norm=hue_norm) 626 p.map_size(sizes=sizes, order=size_order, norm=size_norm) File ~\anaconda3\lib\site-packages\seaborn\relational.py:365, in _LinePlotter.__init__(self, data, variables, estimator, n_boot, seed, errorbar, sort, orient, err_style, err_kws, legend) 351 def __init__( 352 self, *, 353 data=None, variables={}, (...) 359 # the kind of plot to draw, but for the time being we need to set 360 # this information so the SizeMapping can use it 361 self._default_size_range = ( 362 np.r_[.5, 2] * mpl.rcParams["lines.linewidth"] 363 ) --> 365 super().__init__(data=data, variables=variables) 367 self.estimator = estimator 368 self.errorbar = errorbar File ~\anaconda3\lib\site-packages\seaborn\_oldcore.py:640, in VectorPlotter.__init__(self, data, variables) 635 # var_ordered is relevant only for categorical axis variables, and may 636 # be better handled by an internal axis information object that tracks 637 # such information and is set up by the scale_* methods. The analogous 638 # information for numeric axes would be information about log scales. 639 self._var_ordered = {"x": False, "y": False} # alt., used DefaultDict --> 640 self.assign_variables(data, variables) 642 for var, cls in self._semantic_mappings.items(): 643 644 # Create the mapping function 645 map_func = partial(cls.map, plotter=self) File ~\anaconda3\lib\site-packages\seaborn\_oldcore.py:701, in VectorPlotter.assign_variables(self, data, variables) 699 else: 700 self.input_format = "long" --> 701 plot_data, variables = self._assign_variables_longform( 702 data, **variables, 703 ) 705 self.plot_data = plot_data 706 self.variables = variables File ~\anaconda3\lib\site-packages\seaborn\_oldcore.py:938, in VectorPlotter._assign_variables_longform(self, data, **kwargs) 933 elif isinstance(val, (str, bytes)): 934 935 # This looks like a column name but we don't know what it means! 937 err = f"Could not interpret value `{val}` for parameter `{key}`" --> 938 raise ValueError(err) 940 else: 941 942 # Otherwise, assume the value is itself data 943 944 # Raise when data object is present and a vector can't matched 945 if isinstance(data, pd.DataFrame) and not isinstance(val, pd.Series): ValueError: Could not interpret value `Mes` for parameter `x`
<Figure size 1200x600 with 0 Axes>
#Removeremos los meses que no tienen delitos por no estar incluidos, todo lo posterior a Octubre
# Asegúrate de que las fechas sean del tipo datetime
fechas_a_eliminar = pd.to_datetime(["2024-11-01", "2024-12-01"])
# Filtrar los datos
delitos_por_fecha = delitos_por_fecha[~delitos_por_fecha["Fecha"].isin(fechas_a_eliminar)]
#Primeras visualizaciones
import seaborn as sns
import matplotlib.pyplot as plt
# Agrupar por año, mes y calcular la cantidad total de delitos
delitos_por_fecha = data_delic_long.groupby(["Fecha"])["Cantidad"].sum().reset_index()
# Crear un gráfico de línea
plt.figure(figsize=(12, 6))
sns.lineplot(data=delitos_por_fecha, x="Fecha", y="Cantidad", marker="o")
plt.title("Cantidad de delitos a lo largo del tiempo")
plt.xlabel("Fecha")
plt.ylabel("Cantidad de delitos")
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()
delitos_por_fecha.columns
Index(['Fecha', 'Cantidad'], dtype='object')
data_delic_long.filter("Año"==2024)
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) ~\AppData\Local\Temp\ipykernel_21028\3330384519.py in ?() ----> 1 data_delic_long.filter("Año"==2024) ~\anaconda3\lib\site-packages\pandas\core\generic.py in ?(self, items, like, regex, axis) 5534 if items is not None: 5535 name = self._get_axis_name(axis) 5536 # error: Keywords must be strings 5537 return self.reindex( # type: ignore[misc] -> 5538 **{name: [r for r in items if r in labels]} # type: ignore[arg-type] 5539 ) 5540 elif like: 5541 TypeError: 'bool' object is not iterable
C:\Users\diego\AppData\Local\Temp\ipykernel_21028\607123910.py:6: FutureWarning: The `ci` parameter is deprecated. Use `errorbar=None` for the same effect. sns.barplot(x="Entidad", y="Cantidad", data=delitos_por_entidad, estimator="sum", ci=None)
#Se agrega el shapefile de méxico, el cual se obtiene de la biblioteca digital de mapas https://www.inegi.org.mx/app/mapas/
import geopandas as gpd
import pandas as pd
import matplotlib.pyplot as plt
# Cargar el shapefile de México https://www.inegi.org.mx/app/mapas/
shapefile = "00ent.shp"
mexico_map = gpd.read_file(shapefile)
mexico_map.plot()
<Axes: >
#A este punto tengo el dataset de delincuencia y el mapa de méxico por estado y sus datos geográficos
mexico_map.head()
| CVEGEO | CVE_ENT | NOMGEO | geometry | |
|---|---|---|---|---|
| 0 | 01 | 01 | Aguascalientes | POLYGON ((2470517.824 1155028.588, 2470552.248... |
| 1 | 02 | 02 | Baja California | MULTIPOLYGON (((1313480.513 1831458.607, 13135... |
| 2 | 03 | 03 | Baja California Sur | MULTIPOLYGON (((1694656.344 1227647.637, 16946... |
| 3 | 04 | 04 | Campeche | MULTIPOLYGON (((3544897.199 946994.621, 354491... |
| 4 | 05 | 05 | Coahuila de Zaragoza | POLYGON ((2469954.193 1978522.993, 2469982.807... |
delitos_por_estado = data_delic_long.groupby('Entidad')['Cantidad'].sum().reset_index()
data_delic_long['Cantidad'] = data_delic_long['Cantidad'].astype('float32')
data_delic_long.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 27828864 entries, 0 to 27828863 Data columns (total 12 columns): # Column Dtype --- ------ ----- 0 Año int64 1 Clave_Ent int64 2 Entidad object 3 Cve. Municipio int64 4 Municipio object 5 Bien jurídico afectado object 6 Tipo de delito object 7 Subtipo de delito object 8 Modalidad object 9 Mes int64 10 Cantidad float32 11 Fecha datetime64[ns] dtypes: datetime64[ns](1), float32(1), int64(4), object(6) memory usage: 2.4+ GB
#Mapa combinado
# Carga los shapefiles
entidades = gpd.read_file("00ent.shp") # Límites de entidades
municipios = gpd.read_file("00mun.shp") # Límites de municipios
lineas = gpd.read_file("00l.shp") # Líneas (carreteras, ríos, etc.)
area_general = gpd.read_file("00a.shp") # Área general
# Asegúrate de que todas las capas tengan la misma proyección (CRS)
entidades = entidades.to_crs("EPSG:4326")
municipios = municipios.to_crs(entidades.crs)
lineas = lineas.to_crs(entidades.crs)
area_general = area_general.to_crs(entidades.crs)
# Crear el mapa combinando las capas
fig, ax = plt.subplots(figsize=(12, 10))
# Dibuja el área general
area_general.plot(ax=ax, color="lightgray", edgecolor="black", alpha=0.5)
# Dibuja los límites de las entidades
entidades.plot(ax=ax, color="none", edgecolor="blue", linewidth=1)
# Configura el título y oculta los ejes
ax.set_title("Mapa combinado de México (INEGI)", fontsize=16)
ax.axis("off")
# Muestra el mapa
plt.tight_layout()
plt.show()
#Creación de modelo de predicción de delitos por estado y fecha
data_delic_long.head()
| Año | Clave_Ent | Entidad | Cve. Municipio | Municipio | Bien jurídico afectado | Tipo de delito | Subtipo de delito | Modalidad | Mes | Cantidad | Fecha | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 2015 | 1 | Aguascalientes | 1001 | Aguascalientes | La vida y la Integridad corporal | Homicidio | Homicidio doloso | Con arma de fuego | 1 | 2.0 | 2015-01-01 |
| 1 | 2015 | 1 | Aguascalientes | 1001 | Aguascalientes | La vida y la Integridad corporal | Homicidio | Homicidio doloso | Con arma blanca | 1 | 1.0 | 2015-01-01 |
| 2 | 2015 | 1 | Aguascalientes | 1001 | Aguascalientes | La vida y la Integridad corporal | Homicidio | Homicidio doloso | Con otro elemento | 1 | 0.0 | 2015-01-01 |
| 3 | 2015 | 1 | Aguascalientes | 1001 | Aguascalientes | La vida y la Integridad corporal | Homicidio | Homicidio doloso | No especificado | 1 | 1.0 | 2015-01-01 |
| 4 | 2015 | 1 | Aguascalientes | 1001 | Aguascalientes | La vida y la Integridad corporal | Homicidio | Homicidio culposo | Con arma de fuego | 1 | 0.0 | 2015-01-01 |
import xgboost as xgb
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_absolute_error
from sklearn.preprocessing import LabelEncoder
# Instanciamos el encoder
label_encoder = LabelEncoder()
# Codificamos las columnas categóricas
data_delic_long['Entidad'] = label_encoder.fit_transform(data_delic_long['Entidad'])
data_delic_long['Tipo de delito'] = label_encoder.fit_transform(data_delic_long['Tipo de delito'])
# Ver las primeras filas para asegurar la codificación
print(data_delic_long[['Entidad', 'Tipo de delito']].head())
Entidad Tipo de delito 0 0 18 1 0 18 2 0 18 3 0 18 4 0 18
# Definir las características y el objetivo
X = data_delic_long.drop(columns=['Cantidad', 'Fecha', 'Municipio', 'Bien jurídico afectado', 'Subtipo de delito', 'Modalidad']) # Las columnas no relevantes
y = data_delic_long['Cantidad']
#División de conjunto de datos
from sklearn.model_selection import train_test_split
# Dividir el conjunto de datos
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
print(X_train.shape, X_test.shape) # Ver las dimensiones de los conjuntos de entrenamiento y prueba
(22263091, 6) (5565773, 6)
#Entrenamiento
import xgboost as xgb
from sklearn.metrics import mean_squared_error
# Convertir a formato DMatrix que XGBoost entiende bien
dtrain = xgb.DMatrix(X_train, label=y_train)
dtest = xgb.DMatrix(X_test, label=y_test)
# Configuración de parámetros del modelo
params = {
'objective': 'reg:squarederror', # Regresión
'max_depth': 6,
'learning_rate': 0.1,
'eval_metric': 'rmse', # Usamos RMSE para evaluar
'subsample': 0.8, # Proporción de datos a usar en cada árbol
'colsample_bytree': 0.8, # Proporción de columnas
'n_estimators': 100, # Número de árboles
}
# Entrenar el modelo
model = xgb.train(params, dtrain, num_boost_round=100)
# Realizar predicciones en el conjunto de prueba
y_pred = model.predict(dtest)
# Evaluación del modelo
rmse = mean_squared_error(y_test, y_pred, squared=False)
print(f'RMSE: {rmse}')
C:\Users\diego2.gonzalez\AppData\Local\anaconda3\Lib\site-packages\xgboost\core.py:158: UserWarning: [16:18:49] WARNING: C:\buildkite-agent\builds\buildkite-windows-cpu-autoscaling-group-i-0c55ff5f71b100e98-1\xgboost\xgboost-ci-windows\src\learner.cc:740:
Parameters: { "n_estimators" } are not used.
warnings.warn(smsg, UserWarning)
RMSE: 7.586770534515381
C:\Users\diego2.gonzalez\AppData\Local\anaconda3\Lib\site-packages\sklearn\metrics\_regression.py:492: FutureWarning: 'squared' is deprecated in version 1.4 and will be removed in 1.6. To calculate the root mean squared error, use the function'root_mean_squared_error'. warnings.warn(
#Gráfica de predicción vs real
import matplotlib.pyplot as plt
# Gráfico de predicción vs valor real
plt.scatter(y_test, y_pred, alpha=0.3)
plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], '--r', label='Ideal')
plt.xlabel('Valor real')
plt.ylabel('Predicción')
plt.title('Predicción vs Real')
plt.legend()
plt.show()
C:\Users\diego2.gonzalez\AppData\Local\anaconda3\Lib\site-packages\IPython\core\pylabtools.py:170: UserWarning: Creating legend with loc="best" can be slow with large amounts of data. fig.canvas.print_figure(bytes_io, **kw)
#Gráfica de métricas
xgb.plot_importance(model, importance_type='weight', max_num_features=10, title='Top 10 Características Importantes')
plt.show()
# Visualizar las métricas durante el entrenamiento
evals_result = model.eval_set([(dtrain, 'train'), (dtest, 'test')])
print(evals_result)
[0] train-rmse:7.80108055501440045 test-rmse:7.58676898050582516
#Redes neuronales recurrentes
from sklearn.preprocessing import MinMaxScaler
# Normalizar la variable 'Cantidad'
scaler = MinMaxScaler(feature_range=(0, 1))
data_delic_long['Cantidad_scaled'] = scaler.fit_transform(data_delic_long[['Cantidad']])
import numpy as np
def create_sequences(df, sequence_length):
sequences = []
labels = []
for i in range(len(df) - sequence_length):
sequences.append(df.iloc[i:i+sequence_length, :-1].values) # Datos de entrada
labels.append(df.iloc[i+sequence_length, -1]) # Variable objetivo
return np.array(sequences), np.array(labels)
# Usamos 6 meses anteriores para predecir el mes siguiente
sequence_length = 6
X, y = create_sequences(data_delic_long, sequence_length)
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import LSTM, Dense, Dropout
# Definir el modelo LSTM
model = Sequential()
# Capa LSTM
model.add(LSTM(64, activation='relu', input_shape=(X.shape[1], X.shape[2]), return_sequences=False))
# Dropout para prevenir sobreajuste
model.add(Dropout(0.2))
# Capa densa de salida
model.add(Dense(1)) # Una sola salida (predicción de la cantidad)
# Compilar el modelo
model.compile(optimizer='adam', loss='mean_squared_error')
# Dividir los datos en entrenamiento y prueba
train_size = int(len(X) * 0.8) # 80% para entrenamiento
X_train, X_test = X[:train_size], X[train_size:]
y_train, y_test = y[:train_size], y[train_size:]
# Entrenar el modelo
history = model.fit(X_train, y_train, epochs=20, batch_size=64, validation_data=(X_test, y_test))
import matplotlib.pyplot as plt
# Evaluar el modelo en los datos de prueba
y_pred = model.predict(X_test)
# Gráfico de predicciones vs reales
plt.plot(y_test, label='Real')
plt.plot(y_pred, label='Predicción')
plt.legend()
plt.title('Predicción vs Real')
plt.show()
# Calcular el RMSE
from sklearn.metrics import mean_squared_error
rmse = np.sqrt(mean_squared_error(y_test, y_pred))
print(f"RMSE: {rmse}")
# Gráfica de la pérdida
plt.plot(history.history['loss'], label='Entrenamiento')
plt.plot(history.history['val_loss'], label='Validación')
plt.legend()
plt.title('Curva de pérdida')
plt.xlabel('Épocas')
plt.ylabel('Pérdida')
plt.show()
##Creación de red neuronal con Transformer para predicción de delitos por estado
#Librerias
from sklearn.preprocessing import OneHotEncoder
from sklearn.model_selection import train_test_split
import pytorch_forecasting
from pytorch_forecasting import TemporalFusionTransformer
from pytorch_forecasting.data import TimeSeriesDataSet
from pytorch_forecasting.data import GroupNormalizer
# Asegúrate de que la columna 'Fecha' sea de tipo datetime
df['Fecha'] = pd.to_datetime(df['Fecha'])
# Convertir la fecha a un índice entero (por ejemplo, el número de meses desde el inicio del dataset)
df['time_idx'] = (df['Fecha'] - df['Fecha'].min()).dt.days // 30 # Índice de tiempo en meses
# Ahora 'time_idx' es un entero y puede ser utilizado como índice temporal
# Conversión de las variables categóricas
df['Entidad'] = df['Entidad'].astype('category')
df['Tipo de delito'] = df['Tipo de delito'].astype('category')
# Crear una columna para el mes/año para convertirlo en una característica temporal
df['Fecha'] = pd.to_datetime(df['Fecha'])
df['Mes'] = df['Fecha'].dt.month
df['Año'] = df['Fecha'].dt.year
# Agregar las columnas que usaremos para el modelo
df['Cantidad'] = df['Cantidad'].fillna(0) # Rellenar los valores nulos de la cantidad de delitos
# Seleccionar las columnas que vamos a usar
df = df[['Entidad', 'Tipo de delito', 'Fecha', 'Cantidad', 'Mes', 'Año']]
# Codificación de las variables categóricas
encoder = OneHotEncoder(sparse_output=True)
encoded = encoder.fit_transform(df[['Entidad', 'Tipo de delito']])
# Convertir la matriz dispersa a DataFrame
encoded_df = pd.DataFrame.sparse.from_spmatrix(encoded, columns=encoder.get_feature_names_out(['Entidad', 'Tipo de delito']))
df = pd.concat([df, encoded_df], axis=1)
# Estructurar el dataset para temporal forecasting
max_prediction_length = 12 # Número de meses a predecir
max_encoder_length = 24 # Número de meses para el historial
# Crear el TimeSeriesDataSet
training = TimeSeriesDataSet(
df,
time_idx="Fecha",
target="Cantidad",
group_ids=["Entidad", "Tipo de delito"], # Por cada combinación de entidad y tipo de delito
min_encoder_length=max_encoder_length,
max_encoder_length=max_encoder_length,
min_prediction_length=1,
max_prediction_length=max_prediction_length,
static_categoricals=["Entidad", "Tipo de delito"],
time_varying_known_categoricals=["Mes", "Año"], # Mes y año como características
time_varying_known_reals=["Fecha"],
time_varying_unknown_reals=["Cantidad"], # Variable de predicción
add_relative_time_idx=True,
add_target_scales=True,
add_encoder_length=True
)
# Normalización de las características
scaler = GroupNormalizer(groups=["Entidad", "Tipo de delito"], transformation="soft")
training = training.scale(scaler)
# Dividir en entrenamiento y validación
train_data, val_data = training.split_after(0.8)
# Crear el modelo Temporal Fusion Transformer (TFT)
trainer = pytorch_forecasting.models.temporal_fusion_transformer.TemporalFusionTransformer.from_dataset(
train_data,
learning_rate=0.001,
hidden_size=64,
attention_head_size=4,
dropout=0.1,
hidden_continuous_size=32,
output_size=1,
loss=pytorch_forecasting.metrics.MeanAbsoluteError(),
log_interval=10,
reduce_on_plateau_patience=4,
logging_metrics=[pytorch_forecasting.metrics.MeanSquaredError()],
pl_trainer_kwargs={"accelerator": "gpu" if torch.cuda.is_available() else "cpu", "devices": 1}
)
# Ajuste del learning rate (opcional)
trainer.lr_find()
# Entrenar el modelo
trainer.fit(train_data, val_data, epochs=20, batch_size=64)
# Gráfico de Learning Rate
lr_finder = trainer.lr_find()
lr_finder.plot_lr_find()
plt.show()
# Evaluación del modelo en los datos de validación
best_model = trainer.load_best_model()
raw_predictions, x = best_model.predict(val_data, mode="raw", return_x=True)
# Predicción vs Real (Gráfico)
fig, ax = plt.subplots(figsize=(10, 6))
ax.plot(x["Fecha"], x["Cantidad"], label="Real", color='blue')
ax.plot(x["Fecha"], raw_predictions[:, 0], label="Predicción", color='red')
plt.title("Predicción vs Real (Cantidad de Delitos)")
plt.xlabel("Fecha")
plt.ylabel("Cantidad de Delitos")
plt.legend()
plt.show()
--------------------------------------------------------------------------- AssertionError Traceback (most recent call last) Cell In[136], line 29 26 max_encoder_length = 24 # Número de meses para el historial 28 # Crear el TimeSeriesDataSet ---> 29 training = TimeSeriesDataSet( 30 df, 31 time_idx="Fecha", 32 target="Cantidad", 33 group_ids=["Entidad", "Tipo de delito"], # Por cada combinación de entidad y tipo de delito 34 min_encoder_length=max_encoder_length, 35 max_encoder_length=max_encoder_length, 36 min_prediction_length=1, 37 max_prediction_length=max_prediction_length, 38 static_categoricals=["Entidad", "Tipo de delito"], 39 time_varying_known_categoricals=["Mes", "Año"], # Mes y año como características 40 time_varying_known_reals=["Fecha"], 41 time_varying_unknown_reals=["Cantidad"], # Variable de predicción 42 add_relative_time_idx=True, 43 add_target_scales=True, 44 add_encoder_length=True 45 ) 47 # Normalización de las características 48 scaler = GroupNormalizer(groups=["Entidad", "Tipo de delito"], transformation="soft") File ~\AppData\Local\anaconda3\Lib\site-packages\pytorch_forecasting\data\timeseries.py:351, in TimeSeriesDataSet.__init__(self, data, time_idx, target, group_ids, weight, max_encoder_length, min_encoder_length, min_prediction_idx, min_prediction_length, max_prediction_length, static_categoricals, static_reals, time_varying_known_categoricals, time_varying_known_reals, time_varying_unknown_categoricals, time_varying_unknown_reals, variable_groups, constant_fill_strategy, allow_missing_timesteps, lags, add_relative_time_idx, add_target_scales, add_encoder_length, target_normalizer, categorical_encoders, scalers, randomize_length, predict_mode) 349 assert self.min_prediction_length > 0, "min prediction length must be larger than 0" 350 assert isinstance(self.min_prediction_length, int), "min prediction length must be integer" --> 351 assert data[time_idx].dtype.kind == "i", "Timeseries index should be of type integer" 352 self.target = target 353 self.weight = weight AssertionError: Timeseries index should be of type integer
# Preparar datos para entrenamiento con TemporalFusionTransformer
# Agrupar los datos por entidad y por mes para crear las secuencias temporales
max_encoder_length = 36 # Número de meses que el modelo usará para mirar atrás
max_prediction_length = 6 # El modelo predecirá la cantidad de delitos para los siguientes 6 meses
# Crear el dataset de series temporales
training = TimeSeriesDataSet(
df,
time_idx='Mes', # Índice temporal, en este caso el mes
target='Cantidad', # Variable objetivo, cantidad de delitos
group_ids=['Entidad'], # Agrupamos por Entidad
static_categoricals=['Entidad', 'Tipo de delito'], # Variables estáticas categóricas
static_reals=['Año'], # Variables estáticas numéricas
time_varying_known_categoricals=['Mes'], # Variables conocidas que cambian con el tiempo
time_varying_known_reals=['Año'], # Variables numéricas conocidas que cambian con el tiempo
time_varying_unknown_reals=['Cantidad'], # Variable que cambia con el tiempo y es desconocida
target_normalizer=GroupNormalizer(groups=['Entidad'], transformation='softplus'), # Normalización
max_encoder_length=max_encoder_length,
max_prediction_length=max_prediction_length
)
# Dividir los datos en entrenamiento y validación
train_data, val_data = train_test_split(training, test_size=0.2, shuffle=False)
# Definir el modelo
trainer = pytorch_forecasting.models.temporal_fusion_transformer.TemporalFusionTransformer.from_dataset(
train_data,
learning_rate=0.03,
hidden_size=16,
attention_head_size=4,
dropout=0.1,
hidden_continuous_size=8,
output_size=1, # Solo tenemos una salida, la cantidad de delitos
loss=pytorch_forecasting.models.temporal_fusion_transformer.loss.MAE()
)
# Entrenar el modelo
trainer.fit(train_data, val_data, epochs=20, batch_size=64)
# Realizar predicciones
raw_predictions, x = trainer.predict(val_data, mode="raw", return_x=True)
from sklearn.metrics import mean_absolute_error
# Extraer las predicciones reales y las predicciones
true_values = val_data['Cantidad'].values
predictions = raw_predictions['prediction'].values
# Evaluar el rendimiento
mae = mean_absolute_error(true_values, predictions)
print(f'Mean Absolute Error (MAE): {mae}')
df = data_delic_long
# Conversión de las variables categóricas
df['Entidad'] = df['Entidad'].astype('category')
df['Tipo de delito'] = df['Tipo de delito'].astype('category')
# Crear una columna para el mes/año para convertirlo en una característica temporal
df['Fecha'] = pd.to_datetime(df['Fecha'])
df['Mes'] = df['Fecha'].dt.month
df['Año'] = df['Fecha'].dt.year
# Agregar las columnas que usaremos para el modelo
df['Cantidad'] = df['Cantidad'].fillna(0) # Rellenar los valores nulos de la cantidad de delitos
# Crear un encoding de la variable 'Entidad' y 'Tipo de delito'
encoder = OneHotEncoder(sparse=False)
encoded = encoder.fit_transform(df[['Entidad', 'Tipo de delito']])
# Añadir las columnas codificadas a nuestro dataframe original
encoded_df = pd.DataFrame(encoded, columns=encoder.get_feature_names_out(['Entidad', 'Tipo de delito']))
df = pd.concat([df, encoded_df], axis=1)
# Extraer las predicciones y los valores reales
true_values = val_data['Cantidad'].values
predictions = raw_predictions['prediction'].values
# Crear la gráfica
plt.figure(figsize=(12, 6))
# Graficar los valores reales
plt.plot(true_values, label='Valores Reales', color='blue', alpha=0.6)
# Graficar las predicciones
plt.plot(predictions, label='Predicciones', color='red', linestyle='--', alpha=0.6)
# Personalizar el gráfico
plt.title('Predicción vs Valor Real (Cantidad de Delitos)')
plt.xlabel('Índice')
plt.ylabel('Cantidad de Delitos')
plt.legend()
# Mostrar la gráfica
plt.show()